# -*- coding: UTF-8 -*-
"""
@Project ：swift-forge-engine 
@File    ：conf.py
@IDE     ：PyCharm 
@Author  ：Tony.Gan
@Date    ：2025/2/18 21:52 
@Des     ：
"""
import os
from functools import lru_cache
from typing import Any, Literal

from pydantic import model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """全局配置"""
    # 模型配置
    model_config = SettingsConfigDict(
        env_file=f'.env',
        env_file_encoding='utf-8',
        extra='ignore'
    )
    # 环境变量，可以是 dev（开发环境）或 prod（生产环境）
    ENVIRONMENT: Literal['dev', 'prod']

    # 数据库类型，可以是 mysql 或 postgresql
    DATABASE_TYPE: Literal['mysql', 'postgresql']

    # 数据库连接信息
    DATABASE_HOST: str
    DATABASE_PORT: int
    DATABASE_USER: str
    DATABASE_PASSWORD: str

    # 数据库的 schema 和字符集
    DATABASE_SCHEMA: str
    DATABASE_CHARSET: str = 'utf8mb4'

    # Redis 连接信息
    REDIS_HOST: str
    REDIS_PORT: int
    REDIS_PASSWORD: str
    REDIS_DATABASE: int

    # JWT token 的密钥
    TOKEN_SECRET_KEY: str  # 密钥 secrets.token_urlsafe(32)

    # FastAPI 的 API 路径、标题、版本、描述、文档路径和静态文件服务等配置
    FASTAPI_API_V1_PATH: str = '/api/v1'
    FASTAPI_TITLE: str = 'FastAPI'
    FASTAPI_VERSION: str = '0.0.1'
    FASTAPI_DESCRIPTION: str = 'SwiftForge Engine'
    FASTAPI_DOCS_URL: str | None = '/docs'
    FASTAPI_REDOCS_URL: str | None = '/redocs'
    FASTAPI_OPENAPI_URL: str | None = '/openapi'
    FASTAPI_STATIC_FILES: bool = True
    # 接口测试用登录接口
    SWAGGER_LOGIN_URL: str = "/api/v1/sys/login/swagger"  # 接口测试用登录接口

    # 系统标识
    SYSTEM_FLAG: str = 'sfe'

    DATABASE_ECHO: bool = False  # 是否在终端打印数据库查询语句

    # Redis 连接超时时间（秒）
    REDIS_TIMEOUT: int = 5

    # Token
    TOKEN_ALGORITHM: str = 'HS256'  # JWT token 的算法
    TOKEN_EXPIRE_SECONDS: int = 60 * 60 * 24 * 1  # TOKEN过期时间，单位：秒
    TOKEN_REFRESH_EXPIRE_SECONDS: int = 60 * 60 * 24 * 7  # 刷新令牌过期时间，单位：秒

    # token 和 refresh token 在 Redis 中的前缀
    TOKEN_REDIS_PREFIX: str = F'{SYSTEM_FLAG}:token'
    TOKEN_EXTRA_INFO_REDIS_PREFIX: str = f'{SYSTEM_FLAG}:token_extra_info'
    TOKEN_ONLINE_REDIS_PREFIX: str = f'{SYSTEM_FLAG}:token_online'
    TOKEN_REFRESH_REDIS_PREFIX: str = f'{SYSTEM_FLAG}:refresh_token'

    # JWT 和 RBAC 白名单，这些路径不需要进行 token 验证
    TOKEN_REQUEST_PATH_EXCLUDE: list[str] = [  # JWT / RBAC 白名单
        f'{FASTAPI_API_V1_PATH}/sys/login',
        f'{FASTAPI_API_V1_PATH}/sys/token/refresh',
        f'{FASTAPI_API_V1_PATH}/sys/logout',
        f'{FASTAPI_API_V1_PATH}/sys/user/register',
        f'{FASTAPI_API_V1_PATH}/sys/config',
        f'/static',
    ]

    # 用户信息在 Redis 中的前缀和过期时间
    JWT_USER_REDIS_PREFIX: str = f'{SYSTEM_FLAG}:user'
    JWT_USER_REDIS_EXPIRE_SECONDS: int = 60 * 60 * 24 * 7

    # refresh token 的 cookie 名称和过期时间
    COOKIE_REFRESH_TOKEN_KEY: str = f'{SYSTEM_FLAG}_refresh_token'
    COOKIE_REFRESH_TOKEN_EXPIRE_SECONDS: int = TOKEN_REFRESH_EXPIRE_SECONDS

    # 日志的根级别
    LOG_ROOT_LEVEL: str = 'NOTSET'

    # 标准输出和文件输出的日志格式
    LOG_STD_FORMAT: str = (
        '<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</> | <lvl>{level: <8}</> | '
        '<cyan> {correlation_id} </> | <lvl>{message}</>'
    )
    LOG_FILE_FORMAT: str = (
        '<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</> | <lvl>{level: <8}</> | '
        '<cyan> {correlation_id} </> | <lvl>{message}</>'
    )
    # 日志中的 correlation ID 默认值和长度
    LOG_CID_DEFAULT_VALUE: str = '-'
    LOG_CID_UUID_LENGTH: int = 32  # must <= 32

    # 标准输出和标准错误的日志级别
    LOG_STDOUT_LEVEL: str = 'INFO'
    LOG_STDERR_LEVEL: str = 'ERROR'

    # 标准输出和标准错误的日志文件名
    LOG_STDOUT_FILENAME: str = f'{SYSTEM_FLAG}_access.log'
    LOG_STDERR_FILENAME: str = f'{SYSTEM_FLAG}_error.log'

    # 是否启用跨域和访问中间件
    MIDDLEWARE_CORS: bool = True
    MIDDLEWARE_ACCESS: bool = True

    # 请求头中的 trace ID 键名
    TRACE_ID_REQUEST_HEADER_KEY: str = 'X-Request-ID'

    # 跨域配置
    CORS_ALLOWED_ORIGINS: list[str] = ["http://localhost:3006", "http://192.168.1.29:3006", "https://admin.nuoman.com.cn"]
    CORS_EXPOSE_HEADERS: list[str] = [
        TRACE_ID_REQUEST_HEADER_KEY,
    ]

    # 日期时间的时区和格式
    DATETIME_TIMEZONE: str = 'Asia/Shanghai'
    DATETIME_FORMAT: str = '%Y-%m-%d %H:%M:%S'

    # 限流
    REQUEST_LIMITER_REDIS_PREFIX: str = f'{SYSTEM_FLAG}:limiter'

    # 请求IP
    IP_LOCATION_PARSE: Literal['online', 'offline', 'false'] = 'offline'
    IP_LOCATION_REDIS_PREFIX: str = f'{SYSTEM_FLAG}:ip:location'
    IP_LOCATION_EXPIRE_SECONDS: int = 60 * 60 * 24 * 1  # 过期时间，单位：秒

    # 操作日志
    OPERA_LOG_PATH_EXCLUDE: list[str] = [
        '/favicon.ico',
        FASTAPI_DOCS_URL,
        FASTAPI_REDOCS_URL,
        FASTAPI_OPENAPI_URL,
        f'{FASTAPI_API_V1_PATH}/auth/login/swagger',
    ]
    OPERA_LOG_ENCRYPT_TYPE: int = 1  # 0: AES (性能损耗); 1: md5; 2: ItsDangerous; 3: 不加密, others: 替换为 ******
    OPERA_LOG_ENCRYPT_KEY_INCLUDE: list[str] = [  # 将加密接口入参参数对应的值
        'password',
        'old_password',
        'new_password',
        'confirm_password',
    ]

    """
        环境变量的检查
        如果为生产环境则关闭OpenAPI文档
    """
    @classmethod
    @model_validator(mode='before')
    def check_env(cls, values: Any) -> Any:
        if values['APP_ENV'] == 'prod':
            values['OPENAPI_URL'] = None
        return values


@lru_cache
def get_settings() -> Settings:
    """获取全局配置"""
    environment = os.getenv('APP_ENV', 'dev')  # 读取环境变量，默认值为 dev
    env_file = f'.env.{environment}'  # 动态生成配置文件路径
    return Settings(_env_file=env_file)


# 创建配置实例
settings = get_settings()
